﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Nini.Config;
using System.Windows.Forms;

namespace PowerControl
{
    public class MobileDevice : Positionable
    {
        private Antenna antenna;
        private double downlinkPower;
        private double uplinkPower;
        private double uplinkInterferencePower;
        private MobileBehavior behavior;
        private Service serviceType;
        private double linearSpeed;
        private double angularSpeed;
        private double maxTransmittingPower;
        private double minTransmittingPower;

        //TPC Orders queue
        List<int> tpcQueue;
        //

        public double MinTransmittingPower
        {
            get { return minTransmittingPower; }
            set { minTransmittingPower = value; }
        }
        private double nextTransmittingPower;

        public double MaxTransmittingPower
        {
            get { return maxTransmittingPower; }
            set { maxTransmittingPower = value; }
        }

        public double AngularSpeed
        {
            get { return angularSpeed; }
            set { angularSpeed = value; }
        }
        private int ident;

        public Service ServiceType
        {
            get { return serviceType; }
            set { serviceType = value; }
        }

        public Position Position
        {
            get
            {
                return this.Antenna.Position;
            }

            set
            {
                this.Antenna.Position = value;
            }
        }

        public MobileDevice()
        {
            Random rng = new Random(this.GetHashCode());
            this.ident = rng.Next(1000);
            this.antenna = new MobileAntenna();
            this.Behavior = new MobileBehaviorWander();
            this.Speed = 2.0;                           //2 m/s
            this.AngularSpeed = 2.0;                    //2 rad/s
            this.tpcQueue = new List<int>();
        }

        public Antenna Antenna
        {
            get
            {
                return this.antenna;
            }
            set
            {
                this.antenna = value;
            }
        }

        public double DownlinkReceivedPower
        {
            get
            {
                return downlinkPower;
            }
            set
            {
                this.downlinkPower = value;
            }
        }

        public double UplinkEmittedPower
        {
            get
            {
                return uplinkPower;
            }
            set
            {
                this.uplinkPower = value;
            }
        }

        public double UplinkInterference
        {
            get
            {
                return this.uplinkInterferencePower;
            }
            set
            {
                this.uplinkInterferencePower = value;
            }
        }

        public MobileBehavior Behavior
        {
            get
            {
                return this.behavior;
            }
            set
            {
                this.behavior = value;
            }
        }

        public void Step(double DeltaT)
        {
            this.behavior.Step(DeltaT, this);
        }

        public double Speed
        {
            get
            {
                return this.linearSpeed;
            }
            set
            {
                this.linearSpeed = value;
            }
        }

        public int ID
        {
            get
            {
                return this.ident;
            }
            set
            {
                this.ident = value;
            }
        }

        public static MobileDevice FromXMLNode(XmlNode n)
        {
            MobileDevice m = new MobileDevice();

            //Retrieve mobile model
            String mobile_model = n.Attributes.GetNamedItem("model").Value;
            IniConfigSource source = new IniConfigSource(Application.StartupPath + "\\config\\" + mobile_model);
            m.MaxTransmittingPower = Utils.dBToWatt(source.Configs[0].GetDouble("maxpower")) / 1000.0;
            m.MinTransmittingPower = Utils.dBToWatt(source.Configs[0].GetDouble("minpower")) / 1000.0;
            m.Antenna.TransmittingPower = m.MinTransmittingPower;
            m.Antenna.Sensibility = source.Configs[0].GetDouble("sensibility");
            m.Antenna.Gain = source.Configs[0].GetDouble("antennagain");
            //

            for (int i = 0; i < n.ChildNodes.Count; ++i)
            {
                XmlNode minfonode = n.ChildNodes.Item(i);

                //We have id information
                if (minfonode.Name == "id")
                {
                    m.ID = int.Parse(minfonode.InnerText);
                }

                //We have service information
                if (minfonode.Name == "service")
                {
                    m.ServiceType = ServicesManager.Instance.getService(minfonode.InnerText);
                }

                //We have position information
                if (minfonode.Name == "position")
                {
                    for (int j = 0; j < minfonode.ChildNodes.Count; ++j)
                    {
                        XmlNode posinfonode = minfonode.ChildNodes.Item(j);
                        switch (posinfonode.Name)
                        {
                            case "x":
                                m.Position.X = Double.Parse(posinfonode.InnerText);
                                break;
                            case "y":
                                m.Position.Y = Double.Parse(posinfonode.InnerText);
                                break;
                            case "z":
                                m.Position.Z = Double.Parse(posinfonode.InnerText);
                                break;
                            case "pitch":
                                m.Position.Pitch = Double.Parse(posinfonode.InnerText);
                                break;
                            case "yaw":
                                m.Position.Yaw = Double.Parse(posinfonode.InnerText);
                                break;
                            default:
                                break;
                        }
                    }
                }

                //We have beavioral information
                if (minfonode.Name == "behavior")
                {
                    if (minfonode.Attributes.Count == 1 && minfonode.Attributes.Item(0).Name == "type")
                        switch (minfonode.Attributes.Item(0).Value)
                        {
                            case "idle":
                                m.Behavior = new MobileBehaviorIdle();
                                break;
                            case "wander":
                                m.Behavior = new MobileBehaviorWander();
                                break;
                            case "cycle":
                                m.Behavior = new MobileBehaviorCycle();
                                break;
                            default:
                                break;
                        }

                    for (int j = 0; j < minfonode.ChildNodes.Count; ++j)
                    {
                        XmlNode behinfonode = minfonode.ChildNodes.Item(j);
                        switch (behinfonode.Name)
                        {
                            case "linearspeed":
                                m.Speed = Double.Parse(behinfonode.InnerText);
                                break;
                            case "angularspeed":
                                m.AngularSpeed = Double.Parse(behinfonode.InnerText);
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            return m;
        }

        public void OrderTPC(int order)
        {
            Random rng = new Random();

            int length = 1;

            //
            if (tpcQueue.Count < length)
                tpcQueue.Add(order);
            else
            {
                int sum = 0;
                //Sum the orders
                for (int i = 0; i < tpcQueue.Count; ++i)
                {
                    sum += tpcQueue[i];
                }
                //

                float neworder;
                if (sum >= length)
                    neworder = (float)(rng.NextDouble()) + 0.5f;
                else if (sum <= -length)
                    neworder = -((float)(rng.NextDouble()) + 0.5f);
                else
                    neworder = 0;

                //Apply the order
                if (neworder != 0)
                {
                    double delta = Utils.wattTodB(Antenna.TransmittingPower) + ((double)neworder);
                    delta = Utils.dBToWatt(delta);
                    this.nextTransmittingPower = delta;
                }

                //Clear the queue..
                if (tpcQueue.Count >= length)
                    tpcQueue.Clear();
                //
            }
        }
        
        //Not yet implemented
        public void OpenLoopPC()
        {
            double inv = Utils.wattTodB(1.0/this.DownlinkReceivedPower) - this.Antenna.Gain;
            double filtered = Math.Min(Utils.dBToWatt(inv), MaxTransmittingPower);
            filtered = Math.Max(filtered, MinTransmittingPower);
            //Antenna.TransmittingPower = filtered;
            return;
        }

        public void ApplyTPC()
        {
            double filteredShit = nextTransmittingPower;
            filteredShit = Math.Max(this.MinTransmittingPower, filteredShit);
            filteredShit = Math.Min(this.MaxTransmittingPower, filteredShit);
            this.Antenna.TransmittingPower = filteredShit;
        }
    }
}
